Make the glyph cache grow as needed
authorMatthias Clasen <mclasen@redhat.com>
Wed, 20 Sep 2017 03:32:07 +0000 (23:32 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Thu, 21 Sep 2017 03:26:14 +0000 (23:26 -0400)
Make it possible to have more than one texture in the
glyph cache, and create new ones when we run out of space
in the existing ones.

gsk/gskvulkancolortextpipeline.c
gsk/gskvulkanglyphcache.c
gsk/gskvulkanrenderpass.c
gsk/gskvulkantextpipeline.c

index 6f97a8d6968693ccc5ba44c0beecbda6abe274a0..42b4e8c6399a057115c8d76e78df94a7965077b4 100644 (file)
@@ -104,14 +104,14 @@ gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *
                                                     guint                       num_glyphs)
 {
   GskVulkanColorTextInstance *instances = (GskVulkanColorTextInstance *) data;
-  int i
+  int i;
   int count = 0;
   int x_position = 0;
 
   for (i = 0; i < start_glyph; i++)
     x_position += glyphs->glyphs[i].geometry.width;
 
-  for (; i < num_glyphs; i++)
+  for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
     {
       PangoGlyphInfo *gi = &glyphs->glyphs[i];
 
index 56567851c553fcfb1c2980a81352fa02e1bf8065..fc36b0adac0fae30dce3727fc68ead77dbf41dbc 100644 (file)
@@ -8,16 +8,18 @@
 
 #include <graphene.h>
 
-struct _GskVulkanGlyphCache {
-  GObject parent_instance;
-
-  GHashTable *hash_table;
-
+typedef struct {
   cairo_surface_t *surface;
+  GskVulkanImage *image;
   int width, height;
   int x, y, y0;
+} Atlas;
 
-  GskVulkanImage *image;
+struct _GskVulkanGlyphCache {
+  GObject parent_instance;
+
+  GHashTable *hash_table;
+  GPtrArray *atlases;
 };
 
 struct _GskVulkanGlyphCacheClass {
@@ -32,17 +34,41 @@ static gboolean glyph_cache_equal      (gconstpointer v1,
 static void     glyph_cache_key_free   (gpointer      v);
 static void     glyph_cache_value_free (gpointer      v);
 
+static Atlas *
+create_atlas (void)
+{
+  Atlas *atlas;
+
+  atlas = g_new (Atlas, 1);
+  atlas->width = 512;
+  atlas->height = 512;
+  atlas->y0 = 1;
+  atlas->y = 1;
+  atlas->x = 1;
+  atlas->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, atlas->width, atlas->height);
+  atlas->image = NULL;
+
+  return atlas;
+}
+
+static void
+free_atlas (gpointer v)
+{
+  Atlas *atlas = v;
+
+  if (atlas->surface)
+    cairo_surface_destroy (atlas->surface);
+  g_clear_object (&atlas->image);
+  g_free (atlas);
+}
+
 static void
 gsk_vulkan_glyph_cache_init (GskVulkanGlyphCache *cache)
 {
   cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
                                              glyph_cache_key_free, glyph_cache_value_free);
-  cache->width = 1024;
-  cache->height = 1024;
-  cache->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cache->width, cache->height);
-  cache->y0 = 1;
-  cache->y = 1;
-  cache->x = 1;
+  cache->atlases = g_ptr_array_new_with_free_func (free_atlas);
+  g_ptr_array_add (cache->atlases, create_atlas ());
 }
 
 static void
@@ -51,8 +77,7 @@ gsk_vulkan_glyph_cache_finalize (GObject *object)
   GskVulkanGlyphCache *cache = GSK_VULKAN_GLYPH_CACHE (object);
 
   g_hash_table_unref (cache->hash_table);
-  cairo_surface_destroy (cache->surface);
-  g_clear_object (&cache->image);
+  g_ptr_array_unref (cache->atlases);
 
   G_OBJECT_CLASS (gsk_vulkan_glyph_cache_parent_class)->finalize (object);
 }
@@ -112,21 +137,41 @@ add_to_cache (GskVulkanGlyphCache  *cache,
   cairo_t *cr;
   cairo_scaled_font_t *scaled_font;
   cairo_glyph_t cg;
+  Atlas *atlas;
+  int i;
 
-  if (cache->x + value->draw_width + 1 >= cache->width)
+  for (i = 0; i < cache->atlases->len; i++)
     {
-      /* start a new row */
-      cache->y0 = cache->y + 1;
-      cache->x = 1;
+      int x, y, y0;
+
+      atlas = g_ptr_array_index (cache->atlases, i);
+      x = atlas->x;
+      y = atlas->y;
+      y0 = atlas->y0;
+
+      if (atlas->x + value->draw_width + 1 >= atlas->width)
+        {
+          /* start a new row */
+          y0 = y + 1;
+          x = 1;
+        }
+
+      if (y0 + value->draw_height + 1 >= atlas->height)
+        continue;
+
+      atlas->y0 = y0;
+      atlas->x = x;
+      atlas->y = y;
+      break;
     }
 
-  if (cache->y0 + value->draw_height + 1 >= cache->height)
+  if (i == cache->atlases->len)
     {
-      g_critical ("Drats! Out of cache space. We should really handle this");
-      return;
+      atlas = create_atlas ();
+      g_ptr_array_add (cache->atlases, atlas);
     }
 
-  cr = cairo_create (cache->surface);
+  cr = cairo_create (atlas->surface);
 
   scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
   if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
@@ -136,43 +181,25 @@ add_to_cache (GskVulkanGlyphCache  *cache,
   cairo_set_source_rgba (cr, 1, 1, 1, 1);
 
   cg.index = glyph;
-  cg.x = cache->x - value->draw_x;
-  cg.y = cache->y0 - value->draw_y;
+  cg.x = atlas->x - value->draw_x;
+  cg.y = atlas->y0 - value->draw_y;
 
   cairo_show_glyphs (cr, &cg, 1);
 
   cairo_destroy (cr);
 
-  cache->x = cache->x + value->draw_width + 1;
-  cache->y = MAX (cache->y, cache->y0 + value->draw_height + 1);
-
-  value->tx = (cg.x + value->draw_x) / cache->width;
-  value->ty = (cg.y + value->draw_y) / cache->height;
-  value->tw = (float)value->draw_width / cache->width;
-  value->th = (float)value->draw_height / cache->height;
-
-  value->texture_index = 0;
-}
-
-#if 0
-static void
-dump_glyph_cache_stats (GskVulkanGlyphCache *cache)
-{
-  static gint64 time;
-  gint64 now;
-
-  if (!cache->hash_table)
-    return;
+  atlas->x = atlas->x + value->draw_width + 1;
+  atlas->y = MAX (atlas->y, atlas->y0 + value->draw_height + 1);
 
-  now = g_get_monotonic_time ();
-  if (now - time < 1000000)
-    return;
+  value->tx = (cg.x + value->draw_x) / atlas->width;
+  value->ty = (cg.y + value->draw_y) / atlas->height;
+  value->tw = (float)value->draw_width / atlas->width;
+  value->th = (float)value->draw_height / atlas->height;
 
-  time = now;
+  value->texture_index = i;
 
-  cairo_surface_write_to_png (cache->surface, "gsk-glyph-cache.png");
+  g_clear_object (&atlas->image); /* force re-upload */
 }
-#endif
 
 GskVulkanGlyphCache *
 gsk_vulkan_glyph_cache_new (void)
@@ -199,7 +226,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
       GlyphCacheKey *key;
       PangoRectangle ink_rect;
 
-      value = g_new (GskVulkanCachedGlyph, 1);
+      value = g_new0 (GskVulkanCachedGlyph, 1);
 
       pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
       pango_extents_to_pixels (&ink_rect, NULL);
@@ -210,10 +237,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
       value->draw_height = ink_rect.height;
 
       if (ink_rect.width > 0 && ink_rect.height > 0)
-        {
-          add_to_cache (cache, font, glyph, value);
-          g_clear_object (&cache->image);
-        }
+        add_to_cache (cache, font, glyph, value);
 
       key = g_new (GlyphCacheKey, 1);
       key->font = g_object_ref (font);
@@ -230,12 +254,18 @@ gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
                                         GskVulkanUploader   *uploader,
                                         guint                index)
 {
-  if (cache->image == NULL)
-    cache->image = gsk_vulkan_image_new_from_data (uploader,
-                                                   cairo_image_surface_get_data (cache->surface),
-                                                   cairo_image_surface_get_width (cache->surface),
-                                                   cairo_image_surface_get_height (cache->surface),
-                                                   cairo_image_surface_get_stride (cache->surface));
-
-  return cache->image;
+  Atlas *atlas;
+
+  g_return_val_if_fail (index < cache->atlases->len, NULL);
+
+  atlas = g_ptr_array_index (cache->atlases, index);
+
+  if (atlas->image == NULL)
+    atlas->image = gsk_vulkan_image_new_from_data (uploader,
+                                                   cairo_image_surface_get_data (atlas->surface),
+                                                   cairo_image_surface_get_width (atlas->surface),
+                                                   cairo_image_surface_get_height (atlas->surface),
+                                                   cairo_image_surface_get_stride (atlas->surface));
+
+  return atlas->image;
 }
index d6dc158231904c64052071ed79144e5301a62b42..f3b3dea25da1e9d9100f5fcbdd3edf803ded4499 100644 (file)
@@ -71,9 +71,9 @@ struct _GskVulkanOpText
   gsize                vertex_offset; /* offset into vertex buffer */
   gsize                vertex_count; /* number of vertices */
   gsize                descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
-  guint                texture_index;
-  guint                start_glyph;
-  guint                num_glyphs;
+  guint                texture_index; /* index of the texture in the glyph cache */
+  guint                start_glyph; /* the first glyph in nodes glyphstring that we render */
+  guint                num_glyphs; /* number of *non-empty* glyphs (== instances) we render */
 };
 
 struct _GskVulkanOpPushConstants
@@ -217,8 +217,8 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       {
         PangoFont *font = gsk_text_node_get_font (node);
         PangoGlyphString *glyphs = gsk_text_node_get_glyphs (node);
-        PangoGlyph glyph;
         int i;
+        guint count;
         guint texture_index;
         GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
 
@@ -248,23 +248,39 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
           }
         op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
 
-        i = 0;
-        texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyphs->glyphs[0].glyph);
-        while (i < glyphs->num_glyphs)
-          {
-            op.text.start_glyph = i;
-            op.text.texture_index = texture_index;
+        op.text.start_glyph = 0;
+        op.text.texture_index = G_MAXUINT;
 
-            do {
-              i++;
-              glyph = glyphs->glyphs[i].glyph;
-              if (glyph != PANGO_GLYPH_EMPTY && !(glyph & PANGO_GLYPH_UNKNOWN_FLAG))
-                texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, glyph);
-            } while (i < glyphs->num_glyphs && op.text.texture_index == texture_index);
+        for (i = 0, count = 0; i < glyphs->num_glyphs; i++)
+          {
+            PangoGlyphInfo *gi = &glyphs->glyphs[i];
+
+            if (gi->glyph != PANGO_GLYPH_EMPTY && !(gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG))
+              {
+                texture_index = gsk_vulkan_renderer_cache_glyph (renderer, font, gi->glyph);
+                if (op.text.texture_index == G_MAXUINT)
+                  op.text.texture_index = texture_index;
+                if (texture_index != op.text.texture_index)
+                  {
+                     op.text.num_glyphs = count;
+
+                     g_array_append_val (self->render_ops, op);
+
+                     count = 1;
+                     op.text.start_glyph = i;
+                     op.text.texture_index = texture_index;
+                  }
+                else
+                  count++;
+              }
+          }
 
-            op.text.num_glyphs = i - op.text.start_glyph;
+        if (op.text.texture_index != G_MAXUINT && count != 0)
+          {
+            op.text.num_glyphs = count;
             g_array_append_val (self->render_ops, op);
           }
+
         return;
       }
 
index 76c99886890c78899fee028c8691f26a9f8cd3dc..e3295c4f72536a1e9ebf003545497ce35a4377c5 100644 (file)
@@ -112,14 +112,14 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline  *pipeline,
                                               guint                   num_glyphs)
 {
   GskVulkanTextInstance *instances = (GskVulkanTextInstance *) data;
-  int i
+  int i;
   int count = 0;
   int x_position = 0;
 
   for (i = 0; i < start_glyph; i++)
     x_position += glyphs->glyphs[i].geometry.width;
 
-  for (; i < num_glyphs; i++)
+  for (; i < glyphs->num_glyphs && count < num_glyphs; i++)
     {
       PangoGlyphInfo *gi = &glyphs->glyphs[i];
 
@@ -134,6 +134,7 @@ gsk_vulkan_text_pipeline_collect_vertex_data (GskVulkanTextPipeline  *pipeline,
               GskVulkanCachedGlyph *glyph;
 
               glyph = gsk_vulkan_renderer_get_cached_glyph (renderer, font, gi->glyph);
+
               instance->tex_rect[0] = glyph->tx;
               instance->tex_rect[1] = glyph->ty;
               instance->tex_rect[2] = glyph->tw;